﻿#include "bullet.h"

BulletManager bmanager;

PatternControl::PatternControl():
kill_bullet(0), change_speed(0), change_direction(0), rotate(0), change_start_position(0), change_appearance(0), change_behaviour(0), spawn_bullets(0)
{
}

PatternControl::~PatternControl()
{
}

Bullet::Bullet() :
is_lazor(0), blown(0), use_pattern(0)
{
}

Bullet::~Bullet()
{
	//if (use_pattern)
	//	if (pc.change_speed || pc.change_direction || pc.rotate || pc.change_start_position || pc.change_appearance || pc.change_behaviour || pc.spawn_bullets)
	//		delete[] pc.control_params;
}

void Bullet::Render(CL_GraphicContext &gc)
{
	appearance->Render(gc, this);
}

void Bullet::Update(float time)
{
	if (use_pattern)
		if (trigger != pattern->trigger)
			LaunchPatternControl();
	if (!is_lazor)
		prev_position = position;
	if (!behaviour->Update(this, time)) active=0; // returns false if bullet shall be removed
}

void Bullet::LaunchPatternControl()
{
	// Управление пулей из паттерна
	if (pc.kill_bullet)
		active = 0; // Ну всё понятно
	// Дальше подряд идут все параметры по порядку их встречи в массиве параметров
	int param_count = 0;
	float* ctrlp = pc.c_params.get();
	if (pc.change_speed)
	{
		// Меняем скорость пули
		speed = ctrlp[param_count];
		param_count++;
	}
	if (pc.change_direction)
	{
		CL_Angle ang(ctrlp[param_count], cl_degrees);
		direction = CL_Vec2f(cos(ang.to_radians()), sin(ang.to_radians()));
		param_count++;
	}
	if (pc.rotate)
	{
		CL_Angle ang(ctrlp[param_count], cl_degrees);
		orientation += ang;
		direction = CL_Vec2f(cos(orientation.to_radians()), sin(orientation.to_radians()));
		param_count++;
	}
	if (pc.change_start_position)
	{
		start_position += CL_Vec2f(ctrlp[param_count], ctrlp[param_count+1]);
		param_count++;
		param_count++;
	}
	if (pc.change_appearance)
	{
	}
	if (pc.change_behaviour)
	{
	}
	if (pc.spawn_bullets)
	{
		// Создать новые пули на месте текущей
		int b_num = pc.spawn_bullets;
		cl_log_event("Debug", "Spawn %1 bullets", b_num);
		// Пули заданы в формате
		// [speed, angle, param1, start_param1, behavour, appearance, appearance_index] - по 7 параметров на пулю
		for (int b = 0; b < b_num; b++)
		{
			Bullet new_b;
			CL_String bname = CL_String("bullet") + CL_StringHelp::int_to_text((int)ctrlp[param_count + b*7 + 5]);
			new_b = patterns.GetBulletTemplateByName(bname);
			new_b.position = position;
			new_b.prev_position = position;
			new_b.speed = ctrlp[param_count + b*7];
			CL_Angle ang(ctrlp[param_count + b*7 + 1], cl_degrees);
			new_b.orientation = ang;
			new_b.direction = CL_Vec2f(cos(ang.to_radians()), sin(ang.to_radians()));
			new_b.param_float1 = ctrlp[param_count + b*7 + 2];
			new_b.start_param_float1 = ctrlp[param_count + b*7 + 3];
			new_b.start_position = start_position;
			new_b.appearance_index = ctrlp[param_count + b*7 + 6];
			switch ((int)ctrlp[param_count + b*7 + 4])
			{
			case 0:
				new_b.behaviour = patterns.GetBulletBehaviour("bullet_simple");
				break;
			case 1:
				new_b.behaviour = patterns.GetBulletBehaviour("bullet_simple2");
				break;
			case 2:
				new_b.behaviour = patterns.GetBulletBehaviour("bullet_simple2_1");
				break;
			case 3:
				new_b.behaviour = patterns.GetBulletBehaviour("bullet_simple3");
				break;
			default:
				new_b.behaviour = patterns.GetBulletBehaviour("bullet_simple");
				break;
			}
			new_b.blown = 0;
			new_b.grazed = 0;
			new_b.is_lazor = 0;
			new_b.use_pattern = 0;
			new_b.trigger = 0;
			new_b.owner = owner;
			bmanager.Create(new_b);
		}
	}
	trigger = pattern->trigger;
}

void Bullet::CalculateDirection()
{
	direction = CL_Vec2f(cos(orientation.to_radians()), sin(orientation.to_radians()));
}

BulletManager::BulletManager() :
bullets(0), id_index_table(0), active_bullets_count(0), next_free_id_slot(0), damage_sound_time(0)
{
	bullets=new Bullet[MAX_BULLETS];
	id_index_table=new unsigned int[MAX_BULLETS];
	// initialize the ID-index-table to form a linked list, each entry pointing to the free bullet
	for(unsigned int t=0;t<MAX_BULLETS;++t) id_index_table[t]=t+1;
}

BulletManager::~BulletManager()
{
	delete[] id_index_table;
	delete[] bullets;
}

void BulletManager::Clear()
{
	active_bullets_count = 0;
	next_free_id_slot = 0;
	for(unsigned int t=0;t<MAX_BULLETS;++t) id_index_table[t]=t+1;
}

unsigned int BulletManager::Create(Bullet &p_bullet_description)
{
	Bullet &l_new_bullet=bullets[active_bullets_count];
	l_new_bullet = p_bullet_description;
	if (!l_new_bullet.is_lazor)
	{
		l_new_bullet.prev_position = l_new_bullet.position;
		l_new_bullet.grazed = 0;
	}
	else
	{
		l_new_bullet.grazed = 60;
	}
	l_new_bullet.active = 1;
	l_new_bullet.time_lived = 0;
	unsigned int l_id=next_free_id_slot;
	l_new_bullet.id=l_id;
	// remember: next_free_id_slot contains the index of the next free ID, see initialization
	// now we store that index to the free ID list for the next bullet that's going to be created later...
	next_free_id_slot=id_index_table[l_id];
	// ... and instead let the table now point to our bullet's array index!
	id_index_table[l_id]=active_bullets_count++;
	global_state.bullets_launched++;
	return l_id;
}

void BulletManager::Update(float time)
{
	damage_sound_time += time;
	unsigned int l_new_index=0;
	for(unsigned int t=0;t<active_bullets_count;++t)
	{
		Bullet &l_bullet = bullets[t];
		l_bullet.Update(time);
		if(!l_bullet.active)
		{
			if(l_bullet.use_pattern)
			{
				l_bullet.pattern->active_bullets--; // Вычесть из паттерна
				//cl_log_event("Debug", "Pattern %1 bullets remaining : %2", l_bullet.pattern->id, l_bullet.pattern->active_bullets);
			}
			id_index_table[l_bullet.id]=next_free_id_slot;
			next_free_id_slot=l_bullet.id;
		} 
		else
		{
			if(t!=l_new_index)
			{
				bullets[l_new_index]=l_bullet;
				id_index_table[l_bullet.id]=l_new_index;
			}
			l_new_index++;
		}
	}
	active_bullets_count = l_new_index;
}

void BulletManager::Render(CL_GraphicContext &gc, CL_Rect &clip, int who)
{
	gc.set_cliprect(clip);
	for(unsigned int t=0;t<active_bullets_count;++t)
		if (bullets[t].owner == who)
			bullets[t].Render(gc);
	gc.pop_cliprect();
}

Bullet* BulletManager::GetBulletByID(unsigned int p_id)
{
	return &bullets[id_index_table[p_id]];
}

// Столкновения

int BulletManager::CollideEnemy(GameObject *e)
{
	unsigned int total_damage = 0;
	unsigned int l_new_index=0;
	for(unsigned int t=0;t<active_bullets_count;++t)
	{
		Bullet &l_bullet = bullets[t];
		if (!l_bullet.owner)
		{
			bool hit = l_bullet.Collide(e);
			if (hit)
			{
				l_bullet.active = 0;
				total_damage += l_bullet.damage;
				// Добавить ветра за попадание
				if (!l_bullet.blown)
				{
					player.wind += player.wind_shot_gain;
					if (player.wind > 100.0f)
						player.wind = 100.0f;
				}
			}
		}
		if(!l_bullet.active)
		{
			if(l_bullet.use_pattern)
				l_bullet.pattern->active_bullets--; // Вычесть из паттерна
			id_index_table[l_bullet.id]=next_free_id_slot;
			next_free_id_slot=l_bullet.id;
			// Выкинуть эффект попадания и отлетающую частичку
			if (l_bullet.appearance == renders.GetBulletRender("player_bullet1"))
				effects.CreateBulletEffect(l_bullet, 1);
			else if (l_bullet.appearance == renders.GetBulletRender("player_bullet_focus1"))
				effects.CreateBulletEffect(l_bullet, 2);
			else if (l_bullet.appearance == renders.GetBulletRender("player_bullet2"))
				effects.CreateBulletEffect(l_bullet, 4);
			else if (l_bullet.appearance == renders.GetBulletRender("player_bullet_focus2"))
				effects.CreateBulletEffect(l_bullet, 51);
			else if (l_bullet.appearance == renders.GetBulletRender("player_bullet_focus3"))
				effects.CreateBulletEffect(l_bullet, 52);
			else if (l_bullet.appearance == renders.GetBulletRender("player_bullet_focus4"))
				effects.CreateBulletEffect(l_bullet, 53);
			else if (l_bullet.appearance == renders.GetBulletRender("player_bullet_focus5"))
				effects.CreateBulletEffect(l_bullet, 54);
			effects.CreateBulletEffect(l_bullet, 3);
			// Запустить звук попадания
			// if (((Enemy*)e)->boss)
			if (damage_sound_time > 50)
			{
				damage_sound_time = 0;
				smanager.PlaySound("se_damage00");
			}
		}
		else
		{
			if(t!=l_new_index)
			{
				bullets[l_new_index]=l_bullet;
				id_index_table[l_bullet.id]=l_new_index;
			}
			l_new_index++;
		}
	}
	active_bullets_count = l_new_index;
	return total_damage;
}

int BulletManager::CollidePlayer()
{
	unsigned int total_damage = 0;
	unsigned int l_new_index=0;
	for(unsigned int t=0;t<active_bullets_count;++t)
	{
		Bullet &l_bullet = bullets[t];
		if (l_bullet.owner)
		{
			if (l_bullet.Collide(&player))
			{
				if (!player.invicible)
					l_bullet.active = 0;
				total_damage += l_bullet.damage;
			}
		}
		if(!l_bullet.active)
		{
			if(l_bullet.use_pattern)
				l_bullet.pattern->active_bullets--; // Вычесть из паттерна
			id_index_table[l_bullet.id]=next_free_id_slot;
			next_free_id_slot=l_bullet.id;
			effects.CreateBulletEffect(l_bullet, 6);
		} 
		else
		{
			if(t!=l_new_index)
			{
				bullets[l_new_index]=l_bullet;
				id_index_table[l_bullet.id]=l_new_index;
			}
			l_new_index++;
		}
	}
	active_bullets_count = l_new_index;
	return total_damage;
}

void BulletManager::CancelBullets(GameObject *canceller)
{
	unsigned int l_new_index=0;
	for(unsigned int t=0;t<active_bullets_count;++t)
	{
		Bullet &l_bullet = bullets[t];
		if (l_bullet.owner)
		{
			if (l_bullet.Collide(canceller))
				l_bullet.active = 0;
		}
		if(!l_bullet.active)
		{
			if(l_bullet.use_pattern)
				l_bullet.pattern->active_bullets--; // Вычесть из паттерна
			id_index_table[l_bullet.id]=next_free_id_slot;
			next_free_id_slot=l_bullet.id;
			effects.CreateBulletEffect(l_bullet, 0);
		} 
		else
		{
			if(t!=l_new_index)
			{
				bullets[l_new_index]=l_bullet;
				id_index_table[l_bullet.id]=l_new_index;
			}
			l_new_index++;
		}
	}
	active_bullets_count = l_new_index;
}

void BulletManager::CancelBossBullets(int spellwork)
{
	unsigned int l_new_index=0;
	int bscore = 2000;
	for(unsigned int t=0;t<active_bullets_count;++t)
	{
		Bullet &l_bullet = bullets[t];
		if (l_bullet.owner)
		{
			l_bullet.active = 0;
		}
		if(!l_bullet.active)
		{
			if(l_bullet.use_pattern)
				l_bullet.pattern->active_bullets--; // Вычесть из паттерна
			id_index_table[l_bullet.id]=next_free_id_slot;
			next_free_id_slot=l_bullet.id;
			// Добавить очков за пулю
			if (spellwork == 1)
			{
				player.score += bscore;
				// Эффект очков с пули
				Effect b_effect;
				b_effect.appearance_index = 2;
				b_effect.position = l_bullet.position;
				b_effect.param_int = bscore;
				b_effect.speed = bonus_manager.score_time;
				b_effect.appearance = renders.GetEffectRender("score_float");
				b_effect.behaviour = patterns.GetEffectBehaviour("score_float");
				effects.Create(b_effect);
				if (bscore < 8000)
					bscore += 20;
				// Выкинуть звездоочки с пули
				Bonus b_bonus;
				b_bonus.position = l_bullet.position;
				b_bonus.prev_position = l_bullet.position;
				b_bonus.speed = 0;
				b_bonus.bonus_type = 16;
				bonus_manager.Add(b_bonus);
			}
			// Эффект убирания пули
			effects.CreateBulletEffect(l_bullet, 0);
		} 
		else
		{
			if(t!=l_new_index)
			{
				bullets[l_new_index]=l_bullet;
				id_index_table[l_bullet.id]=l_new_index;
			}
			l_new_index++;
		}
	}
	active_bullets_count = l_new_index;
}

void BulletManager::GrazeBullets(float time)
{
	GameObject grazer;
	grazer.position = player.position;
	grazer.prev_position = player.prev_position;
	grazer.collision_size = player.graze_collision_size;
	for(unsigned int t=0;t<active_bullets_count;++t)
	{
		Bullet &l_bullet = bullets[t];
		if (l_bullet.owner && (!l_bullet.grazed || l_bullet.is_lazor))
		{
			if (l_bullet.Collide(&grazer))
			{
				// Прогрейзили
				int grazzzed = 0;
				if (l_bullet.is_lazor)
				{
					l_bullet.grazed += (int)time;
					if (l_bullet.grazed > 60)
					{
						l_bullet.grazed -= 60;
						grazzzed = 1;
					}
				}
				else
				{
					l_bullet.grazed = 1;
					grazzzed = 1;
				}
				if (grazzzed)
				{
					player.graze++;
					// Добавить очков за грейз
					player.score += player.graze_score;
					player.wind += player.wind_graze_gain;
					// Запустить эффект грейза
					Effect graze_effect;
					graze_effect.position = player.position;
					graze_effect.orientation = CL_Angle(rand() % 360, cl_degrees);
					graze_effect.direction = CL_Vec2<float>(cos(graze_effect.orientation.to_radians()), sin(graze_effect.orientation.to_radians()));
					graze_effect.speed = 0.3f;
					graze_effect.param_int = 200; // Время жизни
					graze_effect.param_float = 0.7f; // Размер
					graze_effect.appearance_index = 13;
					graze_effect.appearance = renders.GetEffectRender("graze_particle");
					graze_effect.behaviour = patterns.GetEffectBehaviour("effect_flyoff");
					effects.Create(graze_effect);
					smanager.PlaySound("graze");
				}
			}
		}
	}
}

void BulletManager::BlowBullets(GameObject *p, float time)
{
	Appearance* unblowable = renders.GetBulletRender("bullet2");
	for(unsigned int t=0;t<active_bullets_count;++t)
	{
		Bullet &l_bullet = bullets[t];
		if (l_bullet.Collide(p))
		{
			// Пуля попала под область действия ветра
			if (l_bullet.owner)
			{
				if (!l_bullet.is_lazor && l_bullet.appearance != unblowable)
				{
					// сдуваем
					l_bullet.direction = (l_bullet.position - player.position).normalize();
					l_bullet.behaviour = patterns.GetBulletBehaviour("bullet_spinning");
					l_bullet.speed = player.wind_speed;
					l_bullet.param_float1 = 1.2f; // угловая скорость пули
					l_bullet.owner = 0;
					l_bullet.blown = 1;
					global_state.bullets_blowed++;
					l_bullet.Update(time); // ускоренное движение, когда пуля в области действия ветра
				}
			}
			else
			{
				// уже наша пуля, поддаём газу
				l_bullet.speed += player.wind_accel * time;
				l_bullet.Update(time); // ускоренное движение, когда пуля в области действия ветра
			}
		}
	}
}

void BulletManager::BlowBulletsBomb(float time)
{
	Appearance* unblowable = renders.GetBulletRender("bullet2");
	for(unsigned int t=0;t<active_bullets_count;++t)
	{
		Bullet &l_bullet = bullets[t];
		// Сдуваем все пули бомбой
		if (l_bullet.owner)
		{
			if (!l_bullet.is_lazor && l_bullet.appearance != unblowable)
			{
				// сдуваем
				l_bullet.direction = CL_Vec2f(0, -1.0f);
				l_bullet.behaviour = patterns.GetBulletBehaviour("bullet_spinning");
				l_bullet.speed = player.wind_speed;
				l_bullet.param_float1 = 1.3f; // угловая скорость пули
				l_bullet.owner = 0;
				l_bullet.blown = 1;
				global_state.bullets_blowed++;
				l_bullet.Update(time); // ускоренное движение, когда пуля в области действия ветра
			}
		}
		else
		{
			// уже наша пуля, поддаём газу
			l_bullet.speed += player.wind_accel * time;
			l_bullet.Update(time); // ускоренное движение, когда пуля в области действия ветра
		}
	}
}
